package net.lateeye.search.googlecom;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.InvalidPropertiesFormatException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Properties;

import net.lateeye.search.AbsResultSet;
import net.lateeye.search.AbsSearchEngineClient;
import net.lateeye.search.AbsSearchResultPageTextProcessor;
import net.lateeye.search.SearchResult;
import net.lateeye.search.SearchengineProps;
import net.lateeye.search.SearchenginePropsImpl;
import net.lateeye.util.NoValueSpecifiedException;

import org.apache.http.client.ClientProtocolException;

/**
 * Referred to http://www.fireproject.jp/feature/uzumi/httpclient/intro.html
 * 
 * @author 130s@lateeye.net
 * @deprecated 1/15/2011. Google.com accessor is no longer available, due to the
 *             possible conflict with the terms of service of the company. See
 *             http://code.google.com/p/cse-sql-api/wiki/Accessor_GoogleCom
 *             ?ts=1295123079&updated=Accessor_GoogleCom#Alert
 */
public class GoogleComClient extends AbsSearchEngineClient
{

	/** @since 2007jun6 */
	public String url_prefix_googlesuggest = "complete/search";

	public static String TAGPREFIX_ARROUNDDOC_RESULT_GOOGLE = "<div class=g";

	public GoogleComClient(SearchenginePropsImpl props)
	{
		super(props);
	}

	public static void main(String args[])
	{
		// String query = "GUENTHER HOUSE GIFT ST SAN ANTON";
		// String query = "LOS PUEBLITOS          SAN ANTON";
		// String query = "2417 N Haskell Ave     Dallas";
		String query = null;

		GoogleComClient gc = null;
		try {
			gc = new GoogleComClient(new GoogleClientPropsImpl(args[0]));
			query = args[1];
		} catch (InvalidPropertiesFormatException e1) {
			e1.printStackTrace();
			System.exit(1);
		} catch (NoValueSpecifiedException e) {
			System.err
					.println("Make sure input query string as a second argument. "
							+ "Enclose it with double quot if it's multiple words.");
			e.printStackTrace();
			System.exit(1);
		}
		GoogleComResultSet grs = null;
		try {
			grs = (GoogleComResultSet) gc.executeQuery(query);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		System.out.println("size: " + grs.size());
		SearchResult[] rs = grs.getResults();
		for (int i = 0; i < rs.length; i++) {
			System.out.println("Order: " + rs[i].getOrder() + " Title: "
					+ rs[i].getTitle() + " URL: " + rs[i].getDocumentURI());
		}
	}

	// private final Integer so_timeout = new Integer(15 * 1000);

	/**
	 * Use doGet(String encodedQueryStr) internally.
	 * 
	 * @param string
	 * @return LinkedList
	 * @throws IOException
	 * @throws ClientProtocolException
	 * @see net.lateeye.search.AbsSearchEngineClient#doGet(java.lang.String,
	 *      int)
	 */
	public LinkedList<String> doGet(String queryStr)
			throws ClientProtocolException, IOException
	{
		return this.doGetPrv(queryStr);
	}

	/**
	 * 
	 * @author ISAO Saito <130s@1995.sfc.ne.jp>
	 * @since 2008/01/21
	 * @param encodedQueryStr
	 * @return
	 * @deprecated 2008jan20/130s/need to be impl
	 */
	public String composeGoogleSuggestQuery(String encodedQueryStr)
	{
		return null;
		// this.url = super.URL_SEARCHENGINE + "complete/search"
		// + this.url_prefix_googlesuggest + this.url_hatena
		// + this.url_hl_ja + super.URL_PARAMDELIMITTER + this.url_js_true
		// + super.URL_PARAMDELIMITTER
		// + this.url_identifier_suggest_queryString + encodedQueryStr;
	}

	/**
	 * @param body
	 *            all string what is inside <body> tag
	 * @return list of document URLs
	 */
	protected LinkedList<String> extractDocumentURLs(String body)
	{
		return this.extractDocumentUrl(body, "http");
		// LinkedList urls = new LinkedList();
		// int startPos = 0;
		// String findkey = this.tagPrefix_ArroundDoc;
		// while (0 < body.indexOf(findkey, startPos)) {
		// startPos = body.indexOf(findkey, startPos);
		// int httpPrefixStartPos = body.indexOf("http", startPos);
		// int httpEndPos = body.indexOf("\"", httpPrefixStartPos); //
		// doubleQuote
		// String url = body.substring(httpPrefixStartPos, httpEndPos);
		// urls.add(url);
		// startPos++;
		// }
		// return urls;
	}

	private LinkedList<String> extractDocumentUrl(String body,
			String extractPrefix)
	{
		LinkedList<String> urls = new LinkedList<String>();
		int startPos = 0;
		String findkey = GoogleComClient.TAGPREFIX_ARROUNDDOC_RESULT_GOOGLE;
		while (0 < body.indexOf(findkey, startPos)) {
			startPos = body.indexOf(findkey, startPos);
			int httpPrefixStartPos = body.indexOf(extractPrefix, startPos);
			int httpEndPos = body.indexOf("\"", httpPrefixStartPos); // doubleQuote
			String url = body.substring(httpPrefixStartPos, httpEndPos);
			urls.add(url);
			startPos++;
		}
		return urls;
	}

	/**
	 * 
	 * @author Isao Isaac Saito <130s@lateeye.net>
	 * @deprecated Not implemented.
	 */
	protected AbsResultSet[] executeQueryViaApi(String query)
	{
		return null;
	}

	/**
	 * 
	 * @author Isao Isaac Saito <130s@lateeye.net>
	 * @deprecated Not implemented.
	 */
	public boolean init(LinkedHashMap param)
	{
		return false;
	}

	@Override
	public String doGetHtmlFile(String rawQueryStr)
			throws ClientProtocolException, IOException
	{
		String encodedQueryUrl = this.encodeQueryUrl(rawQueryStr);
		System.out.println(super.getClass().getName()
				+ "#doGetHtmlFile) Encoded URL: " + encodedQueryUrl);
		String body = super.doGetBody(encodedQueryUrl);
		return body;
	}

	@Override
	/*
	 * 
	 * @deprecated Not implemented properly
	 */
	public boolean init(Properties param)
	{
		return true;
	}

	@Override
	/*
	 * 
	 * @deprecated Not implemented properly
	 */
	protected String buildDefaultSearchConditions()
	{
		return null;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * createQueryUrlForSpecificSearchEngine (java.lang.String, int)
	 * 
	 * @deprecated Use createSearchQueryUrl(String, int, int, String)
	 */
	protected String createSearchQueryUrl(String encodedQueryStr)
	{
		return this.createSearchQueryUrl(encodedQueryStr, 0, 0, null);
	}

	/**
	 * 
	 */
	protected String createSearchQueryUrl(String queryStr,
			int requiredResultSize, int startPos, String searchConditions)
			throws NoValueSpecifiedException
	{
		// Sample URL for creation:
		// http://www.google.co.jp/search?hl=en&q=GUENTHER+HOUSE+GIFT+ST+San+Antonio
		// 
		// Use google.co.jp instead of google.com

		String prefix = "";
		String suffix = "";
		String query_startPos = "";
		String query_prefix = "";
		String query_suffix = "";

		try {
			prefix = this.props
					.getProperty(GoogleComProps.PROPKEY_GoogleCOM_URL_PREFIX);
		} catch (NoValueSpecifiedException e) {
			throw e;
		}
		try {
			suffix = this.props
					.getProperty(GoogleComProps.PROPKEY_GoogleCOM_URL_SUFFIX);
		} catch (NoValueSpecifiedException e) {
			// 1/1/11 Do nothing since it's not required according to spec of
			// google.co.jp
		}
		try {
			query_prefix = this.props
					.getProperty(GoogleComProps.PROPKEY_GoogleCOM_URL_QUERY_PREFIX);
		} catch (NoValueSpecifiedException e) {
			// 1/1/11 Do nothing since it's not required according to spec of
			// google.co.jp
		}
		try {
			query_suffix = this.props
					.getProperty(GoogleComProps.PROPKEY_GoogleCOM_URL_QUERY_SUFFIX);
		} catch (NoValueSpecifiedException e) {
			// 1/1/11 Do nothing since it's not required according to spec of
			// google.co.jp
		}
		try {
			query_startPos = this.props
					.getProperty(GoogleComProps.PROPKEY_GoogleCOM_URL_START);
		} catch (NoValueSpecifiedException e) {
			// 1/1/11 Do nothing since it's not required according to spec of
			// google.co.jp
		}
		String delim = this.props
				.getProperty(SearchengineProps.PROPKEY_URL_PARAMDELIMITTER);
		String eq = this.props.getProperty(SearchengineProps.PROPKEY_URL_EQUAL);
		String qUrl = this.props
				.getProperty(SearchengineProps.PROPKEY_URL_ROOT)
				+ this.props
						.getProperty(SearchengineProps.PROPKEY_URL_PARAMDELIMITTER_INITIAL)
				+ prefix
				+ delim
				+ query_prefix
				+ eq
				+ queryStr
				+ query_suffix
				+ delim + suffix
				// + query_startPos + (startPos + 1);
				+ query_startPos;

		System.out.println(super.getClass().getName()
				+ "#createSearchQueryUrl \n\t" + qUrl);

		return qUrl;
	}

	/**
	 * 10Apr07; Un-deprecated since it turns out this method doesn't repeat
	 * forever even if following statement is true. <br />
	 * 
	 * "2008apr4/130s seems this loop and never ends because
	 * super.doGetResultSet(a,b,c,d) called internally and
	 * doGetResultSet(a,b,c,d) calls this method."
	 * 
	 * A property called VAL_NUMOFRESULTS_SINGLEPAGE contributes to it.
	 * 
	 * @Override
	 */
	public AbsResultSet doGetResultSetForSinglePageOfSearchResult(
			String encodedQueryStr, AbsSearchResultPageTextProcessor analyzer)
			throws IOException
	{
		GoogleComResultpageProcessor anlyz = (GoogleComResultpageProcessor) analyzer;

		// String htm = searchGoogleByURLConn(queryStr);
		String htm = super.doGetBody(encodedQueryStr);

		GoogleComResultSet rset = (GoogleComResultSet) anlyz.createResultSet(
				encodedQueryStr, htm);
		return rset;
		// 12/30/2010/130s) Toggled because it's buggy, calling doGetResultSet
		// even though doGetResultSet calls
		// doGetResultSetForSinglePageOfSearchResult (the method we're in now)
		//
		// <toggle from here>
		//
		// // DO: if 100 < num, loop and merge ResuletSet
		// int num_requiredDocs = Integer
		// .parseInt(this.props
		// // .getProperty(SearchengineProps.PROPKEY_VAL_NUM_SEARCHRESULTS));
		// .getProperty(SearchengineProps.PROPKEY_VAL_NUMOFRESULTS_SINGLEPAGE));
		// AbsResultSet rs = null;
		// try {
		// rs = this.doGetResultSet(queryStr, num_requiredDocs, this
		// .buildDefaultSearchConditions(), analyzer);
		// } catch (IOException e) {
		// throw e;
		// }
		// return rs;
		// <toggle til here>

	}

	/**
	 * 
	 * @param queryStr
	 * @return
	 * @deprecated Google returns Http status code 403
	 */
	private String searchGoogleByURLConn(String queryStr)
	{
		StringBuilder result = new StringBuilder();
		try {
			URLConnection conn = new URL(queryStr).openConnection();
			BufferedReader br = new BufferedReader(new InputStreamReader(conn
					.getInputStream(), CHARSET_UTF));
			String line = null;
			// int c = 0;
			while ((line = br.readLine()) != null) {
				result.append(line);
				// System.out.println("Debug: line = " + c + " " + line);
				// c++;
			}
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		String htm = result.toString();
		return htm;
	}

	@Override
	protected AbsSearchResultPageTextProcessor getAnalyzer()
	{
		return new GoogleComResultpageProcessor();
	}

	/**
	 * @deprecated Not implemented yet
	 */
	public boolean isClosed() throws SQLException
	{
		return false;
	}

	/**
	 * @deprecated Not implemented yet
	 */
	public boolean isPoolable() throws SQLException
	{
		return false;
	}

	/**
	 * @deprecated Not implemented yet
	 */
	public void setPoolable(boolean arg0) throws SQLException
	{
	}

	/**
	 * @deprecated Not implemented yet
	 */
	public boolean isWrapperFor(Class<?> arg0) throws SQLException
	{
		return false;
	}

	/**
	 * @deprecated Not implemented yet
	 */
	public <T> T unwrap(Class<T> arg0) throws SQLException
	{
		return null;
	}
}